#ifndef __FILEIO_H__
#define __FILEIO_H__
#include <cerrno>
#include <fcntl.h>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <iostream>
#include "ojData.h"
#include "log.h"
#include <unistd.h>
#include <libgen.h>
#include <string.h>
#include <sys/file.h>
#include <filesystem>

using std::string;


//根据flag字符串切分字符
std::vector<std::string> split(std::string s, std::string flag = "");
std::string replace(std::string s, std::string old_s, std::string new_s, int n = 1e9 + 7);
bool endWith(const string& a, const string& b);
bool startWith(const string& a, const string& b);
//字符串转16进制字符串,适用于所有字符（包括中文）
std::string encodeHexString(const std::string &str);
std::string encodeHexString(const char *str, int len);

//根据文本中的空白符将所有的非空白符分组
std::vector<std::string> fileSplitToVectorByBlank(std::string filename);
std::vector<std::string> fileSplitToVectorByNonBlank(std::string filename);
std::string fileToStr(std::string filename, unsigned int size = 0);
int strToFile(std::string filename, std::string data, std::ios_base::openmode flag = std::ios::out | std::ios::trunc);
std::string fileBlankToStr(std::string filename);
std::string fileNonBlankToStr(std::string filename);
std::string getTempDirectory(const std::string& format);

template <class... Outs>
void readAndMultiWrite(int in, Outs... outs) {
    char buff[1024] = {0};
    int ret = 0;
    auto multi_wirte = [](auto&& self, const char* data, int len, int out, auto... outs) {
        write(out, data, len);
        if constexpr (sizeof...(outs) > 0) {
            self(self, data, len, outs...);
        }
    };
    while ((ret = read(in, buff, 1024)) > 0) {
        multi_wirte(multi_wirte, buff, ret, outs...);
    }
}

// out_fd无限制
// in,pipe_outs必须是管道
template <class... Outs>
void readAndMultiWriteUseLinuxFunc(int in, int out, Outs... pipe_outs) {
    const int BUFF_SIZE = 1024;
    int flags = SPLICE_F_MOVE;
    while (true) {
        int len = BUFF_SIZE;
        Log::instance().getOutput() << "wati tee " << std::endl;
        long long rets[] = { (len = tee(in, pipe_outs, len, 0))... };
        flags = SPLICE_F_MOVE;
        if (len == 0) {
            Log::instance().getOutput() << "len == 0, from in_fd: " << in << ",tee ret: 0, exit" << std::endl;
            break;
        }
        if (std::size(rets) > 0) {
            // if (rets[0] == BUFF_SIZE) {
            //     flags = SPLICE_F_MORE | SPLICE_F_MOVE;
            // }
        }
        Log::instance().getOutput() << "from in_fd: " << in << ",tee rets: ";
        for (auto ret : rets) {
            Log::instance().getOutput() << ret << " ";
        }
        Log::instance().getOutput() << std::endl;
        Log::instance().getOutput() << "pre from in_fd: " << in << " -> " << out << ", len: " << len << std::endl;
        auto ret = splice(in, NULL, out, NULL, len, flags);
        Log::instance().getOutput() << "from in_fd: " << in << ", splice pipe data size: " << ret << std::endl;
        if (ret == 0) {
            Log::instance().getOutput() << "move final" << std::endl;
            break;
        }
        if (ret < 0) {
            Log::instance().getOutput() << "move over: " << ret << std::endl;
            Log::instance().getOutput() << "errno: " << errno << ", strerror: " << strerror(errno) << std::endl;
            break;
        }
    }
}

//合并路径
inline std::string osJoin(const std::string& path, const std::string& file)
{
    return std::filesystem::path(path).append(file).string();
}

//获取文件最近更新时间
int getLastUpdateTime(std::string filename);
int getFileSize(std::string filename);
bool existsFile(std::string filename);
bool IsSameFile(const char *a, const char *b);
bool isSameContentFile(const std::string& f1, const std::string& f2, std::ios::openmode mode = std::ios::in);
bool cp(const std::string& src, const std::string& dst);
bool isDirPath(const std::string& path);
bool mkMultiDir(const std::string& path, int mode);
void createFileInMultiDir(const std::string& file);
inline std::string getDirName(const std::string& path)
{
    // 因为dirname函数会修改入参,所以先拷贝一份
    char temp[128];
    strcpy(temp, path.c_str());
    return std::string(dirname(temp));
}
inline std::string getOriginFilename(const std::string& path)
{
    // 因为basename函数会修改入参,所以先拷贝一份
    char temp[128];
    strcpy(temp, path.c_str());
    return std::string(basename(temp));
}

void fClose();
//设置文件定向
std::vector<std::string> searchFiles(std::string path, std::string suffix = "", bool (*check) (const string&, const string&) = endWith);
void fRedirectAppend(std::string inFile, std::string outFile, std::string errorFile);
void fRedirectWrite(std::string inFile, std::string outFile, std::string errorFile);
std::string commandAppendIOE(std::string command, std::string inFile, std::string outFile, std::string errorFile);

bool isBlank(char c);

//比较两个浮点字符串，如果其中一个不能转为浮点数，或两个数字转为浮点数后的误差超过了eps则返回0，否则返回1
int compareFloatStr(std::string a, std::string b, double eps);

// 从istream中获取一个字符，并把\r,\r\n,\n都转为\n返回，若无字符则返回-1
template <class T>
int getCharUnifineNewLine(T&& s)
{
    if (s.eof() || s.bad())
        return -1;
    char c = s.get();
    char t;
    if (c == '\r' && !s.eof()) {
        t = s.peek();
        if (t == '\n') {
            c = s.get();
        } else {
            c = '\n';
        }
    }
    return c;
}

// 从istream中读入一个连续的空字符串，若无则返回空字符串，同样会统一换行符
template <class T>
std::string getBlankStr(T&& s)
{
    if (s.eof() || s.bad())
        return "";
    std::string str("");
    char c;
    while(!s.eof() && !isBlank(s.peek())) {
        s.get();
    }
    while(!s.eof()) {
        c = s.get();
        if (!isBlank(c)) {
            break;
        } else {
            str.push_back(c);
        }
    }
    str = replace(str, "\r\n", "\n");
    str = replace(str, "\r", "\n");
    return str;
}

// 从istream中读取并输入到文件描述符中
template <class T>
int streamToFileno(T&& s, int fileno)
{
    char buff[MAX_PIPEBUFF];
    int write_size = 0;
    int len = 0;
    if (!s.good()) {
        return -1;
    }
    while(true) {
        s.read(buff, MAX_PIPEBUFF);
        len = s.gcount();
        if (len < 0) {
            return -1;
        } else if (len == 0) {
            break;
        }
        write_size += write(fileno, buff, len);
    }
    return write_size;
}


//根据flag标识进行两个输入流的对比，文件一致返回AC，不一致返回WA，格式错误返回PE，出现意外错误退出进程，退出码为SYS_ERROR
template<class T1, class T2>
int compareIStream(T1&& s1, T2&& s2, int flag = STRICT_CNSISTENT, double eps = 1e-6)
{
    // Log::instance().getOutput() << f1 << " " << f2 << endl;
    // Log::instance().getOutput() << v1.size() << " " << v2.size() << endl;
    s1.sync_with_stdio(false);
    s2.sync_with_stdio(false);

    if (flag == STRICT_CNSISTENT)
    {
        char c1 = ' ', c2 = ' ';
        char t;
        while(!s1.eof() && !s2.eof()) {
            c1 = getCharUnifineNewLine(s1);
            c2 = getCharUnifineNewLine(s2);
            // 大多数情况不进入该if
            if (c1 != c2) {
                return WA;
            }
        }
        // 如果存在某一个流没读完
        if (!s1.eof() || !s2.eof()) {
            return WA;
        }
        // Log::instance().getOutput() << a.length() << " " << a << endl
        //                             << b.length() << " " << b << endl;
        return RUN_OK;
    }
    bool ignoreCase = flag & IGNORE_CASE;
    std::function<bool(const std::string&, const std::string&)> compareStr;
    if (ignoreCase) {
        compareStr = [](const std::string& str1, const std::string& str2)
        { 
            if (str1.length() != str2.length())
                return false;
            for (int i = 0; i < str1.length(); i++) {
                if (::tolower(str1[i]) != ::tolower(str2[i])) {
                    return false;
                }
            }
            return true;
        };
    } else {
        compareStr = [](const std::string& str1, const std::string& str2)
        { return str1 == str2; };
    }
    //此处只用于判断WA，默认该if结束之后的代码可见文本是正确的，之后都是检查格式的选项
    if (flag & ALLOW_PRECISION_ERROR) //精度特判
    {
        // cout << "进行精度特判" << endl;
        std::string str1, str2;
        while (!s1.eof() && !s2.eof()) {
            s1 >> str1;
            s2 >> str2;
            if (!compareStr(str1, str2) && !compareFloatStr(str1, str2, eps)) {
                return WA;
            }
        }
    }
    else //直接字符对比
    {
        std::string str1, str2;
        while (!s1.eof() && !s2.eof()) {
            s1 >> str1;
            s2 >> str2;
            if (!compareStr(str1, str2)) {
                Log::instance().getError()  << "可见字符比对失败" << std::endl
                                            << "[" << str1 << "]" << std::endl
                                            << "[" << str2 << "]" << std::endl;
                return WA;
            }
        }
    }

    // 清除空白符
    while (!s1.eof() && isBlank(s1.peek())) {
        s1.get();
    }
    while (!s2.eof() && isBlank(s2.peek())) {
        s2.get();
    }
    // 如果还有可见字符
    if (!s1.eof() || !s2.eof()) {
        Log::instance().getError()  << "流剩余未读完:" << s1.eof() << "<>" << s2.eof() << std::endl;
        return WA;
    }

    // 复位输入流
    s1.clear();
    s1.seekg(0, std::ios::beg);
    s2.clear();
    s2.seekg(0, std::ios::beg);

    //忽略所有空白符，那么直接返回AC即可
    if (flag & IGNORE_ALL_BLANK) {
        return RUN_OK;
    }
    //对每一段空白符做单独的处理
    if (flag & IGNORE_END_BLANK) {
        std::string str1, str2;
        while (!s1.eof() && !s2.eof()) {
            str1 = getBlankStr(s1);
            str2 = getBlankStr(s2);
            // 以换行符结尾的字符串讲被压缩为一个换行符
            if (str1.size() && str1.back() == '\n') {
                str1 = "\n";
            }
            if (str2.size() && str2.back() == '\n') {
                str2 = "\n";
            }

            // 最后一个不可见字符串将被压缩为空串
            if (s1.eof()) {
                str1 = "";
            }
            if (s2.eof()) {
                str2 = "";
            }

            if (str1 != str2) {
                Log::instance().getError()  << "IGNORE_END_BLANK空白符比较不一致:" << std::endl
                                            << str1.length() << ":" << str1 << std::endl
                                            << str2.length() << ":" << str2 << std::endl;
                return PE;
            }
        }
        if (!s1.eof() || !s2.eof()) {
            return PE;
        }
    } else { // 如果不忽略末尾空格
        std::string str1, str2;
        while (!s1.eof() && !s2.eof()) {
            str1 = getBlankStr(s1);
            str2 = getBlankStr(s2);
            if (str1 != str2)
                return PE;
        }
        if (!s1.eof() || !s2.eof()) {
            return PE;
        }
    }
    return RUN_OK;
}

template <class... Args>
class FileLock {
    int fds[sizeof...(Args)];
    int N = sizeof...(Args);
    std::vector<std::string> files;
public:
    FileLock(int flag, Args&&... args) {
        files = {args...};
        for (int i = 0; i < N; i++) {
            fds[i] = -1;
            fds[i] = open(std::string(files[i]).c_str(), std::ios::in);
            if (fds[i] < 0) {
                Log::instance().getError() << files[i]  << " open error(" << errno << "):" << strerror(errno) << std::endl;
            }
        }

        while (true) {
            int i;
            for (i = 0; i < N; i++) {
                if (fds[i] < 0) {
                    continue;
                }
                if (flock(fds[i], flag | LOCK_NB) < 0) {
                    // Log::instance().getError() << files[i]  << " acquire lock error(" << errno << "):" << strerror(errno) << std::endl;
                    break;
                }
            }
            if (i == N) {
                break;
            }
            release_lock(i);
        }
    }

    void release_lock(int n) {
        for (int i = 0; i < n; i++) {
            int fd = fds[i];
            if (fd >= 0) {
                if (flock(fd, LOCK_UN) < 0) {
                    Log::instance().getError() << files[i]  << " release lock error(" << errno << "):" << strerror(errno) << std::endl;
                }
            }
        }
    }

    ~FileLock() {
        release_lock(N);
        for (int i = 0; i < N; i++) {
            if (fds[i] >= 0) {
                close(fds[i]);
            }
        }
    }
};

template <class... Args>
FileLock(int, Args&&...) -> FileLock<Args...>;

#endif /* __FILEIO_H__ */
